#include "lib_standard.fxh"

struct tProcPrama_Noise {

	float amplitude;
	float frequency;
	float octaves;
	float threshold;
} 
noiseParam_decal,
noiseParam_normal,
noiseParam_specular;

struct tProcPrama_Checker {

	float contrast;
} 
checkerParam_decal,
checkerParam_normal,
checkerParam_specular;

struct tProcPrama_Wood {

	float age_count;
	float age_spacing;
}
woodParam_decal,
woodParam_normal,
woodParam_specular;

struct tProcPrama_Grid {

	float amplitude;
	float height;
	float width;
}
gridParam_decal,
gridParam_normal,
gridParam_specular;

struct tProcPrama_Ramp {

	float wave;
}
rampParam_decal,
rampParam_normal,
rampParam_specular;

//Checker
float3 Procedural_Checker(float3 uvw, tProcPrama_Checker param) {

	uvw = frac(uvw);
	float x = (uvw.x < 0.5f) ? 0.0f : 1.0f;
	float y = (uvw.y < 0.5f) ? 0.0f : 1.0f;
	float z = (uvw.z < 0.5f) ? 0.0f : 1.0f;

	float halfContrast = param.contrast * 0.5f;
	float i = 0.5f + ((x + y + z == 1.0f) ? halfContrast : -halfContrast);
	return float3(i,i,i);
}

//Grid
float3 Procedural_Grid(float3 uvw, tProcPrama_Grid param) {

float GRID_WIDTH = param.height;
	float i = clamp(dot(10.0f ,max(0.0f, frac(uvw) - (1.0f - GRID_WIDTH)) * (1.0f / GRID_WIDTH)), 0, 1);
	
	i = ((i - 0.5f) * param.amplitude) + .5;
	return float3(i,i,i);
}

//Ramp
float3 Procedural_Ramp(float3 uvw, tProcPrama_Ramp param) {

	return frac(float3(uvw.x,uvw.x,uvw.x));
}

//Wood
float3 Procedural_Wood(float3 uvw, tProcPrama_Wood param) {

	return float3(0.0f,0.0f,0.0f);
}
#define baseDim 8.0f
float2 ClampLookupBoundry(float2 coord) {

	return (round(frac(coord - (0.5f / baseDim)) * baseDim).xy * (1.0f / baseDim));
}

float Curve(float t)
{
	return (3 * t * t) - (2 * t * t * t);
}

//Noise
float3 Procedural_Noise(float3 uvw, tProcPrama_Noise param) {

	const float invBaseDim = 1.0f / baseDim;

	#define BASE_DIM_FLOAT 1.0f
	#define INV_BD (1.0f / BASE_DIM_FLOAT)
	
	float4 color = 0.0f;

	float2 uvCoord = uvw.xy * 1;
	//float o = 1.0f;
	//while(o > 0.0f) {

		float2 clamped_boundry_uv = ClampLookupBoundry(uvCoord);

		float2 vector_sample_TL = (float2)Texture_BaseData(clamped_boundry_uv + float2(0.0f, 0.0f));
		float2 vector_sample_TR = (float2)Texture_BaseData(clamped_boundry_uv + float2(invBaseDim, 0.0f));
		float2 vector_sample_BR = (float2)Texture_BaseData(clamped_boundry_uv + float2(invBaseDim, invBaseDim));
		float2 vector_sample_BL = (float2)Texture_BaseData(clamped_boundry_uv + float2(0.0f, invBaseDim));

		vector_sample_TL = (vector_sample_TL - float2(.5,.5)).xy;
		vector_sample_TR = (vector_sample_TR - float2(.5,.5)).xy;
		vector_sample_BR = (vector_sample_BR - float2(.5,.5)).xy;
		vector_sample_BL = (vector_sample_BL - float2(.5,.5)).xy;

		float2 g_frac = frac(uvCoord * baseDim);
		float2 gv_TL = (float2(g_frac.x, g_frac.y));
		float2 gv_TR = (float2(1.0f - g_frac.x, g_frac.y));
		float2 gv_BL = (float2(g_frac.x, 1.0f - g_frac.y));
		float2 gv_BR = (float2(1.0f - g_frac.x, 1.0f - g_frac.y));

		float d_TL = vector_sample_TL;//dot(gv_TL, vector_sample_TL);
		float d_TR = vector_sample_TR;//dot(gv_TR, vector_sample_TR);
		float d_BL = vector_sample_BL;//dot(gv_BL, vector_sample_BL);
		float d_BR = vector_sample_BR;//dot(gv_BR, vector_sample_BR);

		float2 CurveWeights = float2(Curve(g_frac.x),Curve(g_frac.y));

		float final = lerp(lerp(d_TL, d_TR, CurveWeights.x), lerp(d_BL, d_BR, CurveWeights.x), CurveWeights.y);
		
		color = final;
		//color=0;
		//color.xy = length(gv_TL ) * length(gv_TR ) * length(gv_BL ) * length(gv_BR );

		/*if(frac(uvCoord.x) > 0.985)
			color.x = 1;
		if(frac(uvCoord.y) > 0.985)
			color.x = 1;*/

	//	o -= 1;
//color = 0;
//color.xyz = frac(uvCoord.x*32) * Texture_BaseData(uvCoord).xyz;
	//}
	return (color + .5) * .5;// * param.amplitude;
}

//Combines a procedural evaluator for a specific channel
void Channel_Combiner(int channel, float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {
 
	float denominator = 0.0f;
	result = (float3)0.0f;

	float3 coordinates = 
#ifdef DIMENSIONS_3D
		position_os;
#else
		float3(uv_ts, 0.0f);
#endif
	
	if( false ) {}
	#ifdef CHANNEL_DECAL
	else if (channel == CHANNEL_DECAL)
	{	
		#ifdef SAMPLE_TEXTURE_CHANNEL_DECAL
			result += Texture_Unit0(float3(uv_ts, 0.0f));
			denominator++;
		#endif
		#ifdef SAMPLE_CHECKER_CHANNEL_DECAL
			result += Procedural_Checker(coordinates, checkerParam_decal);
			denominator++;
		#endif
		#ifdef SAMPLE_RAMP_CHANNEL_DECAL
			result += Procedural_Ramp(coordinates, rampParam_decal);
			denominator++;
		#endif
		#ifdef SAMPLE_GRID_CHANNEL_DECAL
			result += Procedural_Grid(coordinates, gridParam_decal);
			denominator++;
		#endif
		#ifdef SAMPLE_WOOD_CHANNEL_DECAL
			result += Procedural_Wood(coordinates, woodParam_decal);
			denominator++;
		#endif
		#ifdef SAMPLE_NOISE_CHANNEL_DECAL
			result += Procedural_Noise(coordinates, noiseParam_decal);
			denominator++;
		#endif
	}
	#endif
	#ifdef CHANNEL_NORMAL
	else if (channel == CHANNEL_NORMAL)
	{
		#ifdef SAMPLE_TEXTURE_CHANNEL_NORMAL
			result += Texture_Unit1(float3(uv_ts, 0.0f));
			denominator++;
		#endif
		#ifdef SAMPLE_CHECKER_CHANNEL_NORMAL
			result += Procedural_Checker(coordinates, checkerParam_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_RAMP_CHANNEL_NORMAL
			result += Procedural_Ramp(coordinates, rampParam_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_GRID_CHANNEL_NORMAL
			result += Procedural_Grid(coordinates, gridParam_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_WOOD_CHANNEL_NORMAL
			result += Procedural_Wood(coordinates, woodParam_normal);
			denominator++;
		#endif
		#ifdef SAMPLE_NOISE_CHANNEL_NORMAL
			result += Procedural_Noise(coordinates, noiseParam_normal);
			denominator++;
		#endif
	}
	#endif
	#ifdef CHANNEL_SPECULAR
	else if (channel == CHANNEL_SPECULAR)
	{
		#ifdef SAMPLE_TEXTURE_CHANNEL_SPECULAR
			result += Texture_Unit2(float3(uv_ts, 0.0f));
			denominator++;
		#endif
		#ifdef SAMPLE_CHECKER_CHANNEL_SPECULAR
			result += Procedural_Checker(coordinates, checkerParam_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_RAMP_CHANNEL_SPECULAR
			result += Procedural_Ramp(coordinates, rampParam_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_GRID_CHANNEL_SPECULAR
			result += Procedural_Grid(coordinates, gridParam_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_WOOD_CHANNEL_SPECULAR
			result += Procedural_Wood(coordinates, woodParam_specular);
			denominator++;
		#endif
		#ifdef SAMPLE_NOISE_CHANNEL_SPECULAR
			result += Procedural_Noise(coordinates, noiseParam_specular);
			denominator++;
		#endif
	}
	#endif
	else 
	{
		result = (float3)0.0f;
		result.x = 1;
		denominator = 1.0f;
	}

	denominator = 1.0f / denominator;
	result = isnan(denominator) ? (float3)0.0f : (denominator * result);
}

void Decal_Combiner(float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {

#ifdef CHANNEL_DECAL
	Channel_Combiner(CHANNEL_DECAL, position_os, normal_ws, uv_ts, result);
#else
	result = float3(1.0f,1.0f,1.0f);
#endif
 
}
 
void Normal_Combiner(float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {

#ifdef CHANNEL_NORMAL
	Channel_Combiner(CHANNEL_NORMAL, position_os, normal_ws, uv_ts, result);
#else
	result = float3(1.0f,1.0f,1.0f);
#endif
 
}
 
void Specular_Combiner(float3 position_os, float3 normal_ws, float2 uv_ts, out float3 result) {

#ifdef CHANNEL_SPECULAR
	Channel_Combiner(CHANNEL_SPECULAR, position_os, normal_ws, uv_ts, result);
#else
	result = float3(1.0f,1.0f,1.0f);
#endif
 
}